"""资产项"""
import os
from functools import cache

import bpy
from bpy.props import CollectionProperty, StringProperty, IntProperty, FloatProperty, BoolProperty
from bpy.types import PropertyGroup

from .prefer import Preferences, previews_icons
from ..log import log


class AssetDraw:

    def draw(self, layout: 'bpy.types.UILayout') -> None:
        """
        TODO 绘制资产项
        """
        box = layout.box()
        column = box.column(align=True)
        self.draw_image(column)

        draw_func = getattr(self, f'draw_{self.pref.mode.lower()}')
        if draw_func:
            draw_func(box)
        else:
            box.label(text=f'eee not find   {self.pref.mode}')

    def draw_image(self, layout: 'bpy.types.UILayout'):
        """绘制预览图片"""
        layout.template_icon(
            icon_value=self.icon.icon_id,
            scale=self.pref.icon_size,
        )

    def draw_active(self, layout: 'bpy.types.UILayout'):
        """只绘制图片和名称
        且名称只是显示,不做其它操作,用于显示活动项
        """
        box = layout.box()
        column = box.column(align=True)
        self.draw_image(column)
        row = column.row()
        row.alignment = 'CENTER'
        row.label(text=self.asset_name)

    def draw_pool(self, layout: 'bpy.types.UILayout'):
        """绘制图片
        并显示已选中属性
        """
        layout.active = self.selected
        row = layout.row(align=True)
        row.prop(self, 'selected',
                 text=self.asset_name,
                 toggle=0,
                 emboss=False,
                 icon='RESTRICT_SELECT_OFF' if self.selected else 'RESTRICT_SELECT_ON',
                 full_event=True,
                 event=True,
                 )

    def draw_asset(self, layout: 'bpy.types.UILayout'):
        from ...ops import asset
        op = layout.operator(asset.ImportAsset.bl_idname,
                             text=self.asset_name)
        op.asset_path = self.asset_path
        op.asset_type = self.asset_type

    #     '''绘制资产部分'''
    def draw_object(self, layout: 'bpy.types.UILayout'):
        self.draw_select_asset(layout)

    def draw_scene(self, layout: 'bpy.types.UILayout'):
        self.draw_select_asset(layout)

    def draw_select_asset(self, layout: 'bpy.types.UILayout'):
        """将活动索引设置为当前项
        bpy.ops.wm.context_set_int(data_path="", value=0, relative=False)
        bpy.context.preferences.addons['render_master'].preferences.asset_pointer.asset_items.asset_scene_index = 2
        """
        show_type = self.pref.show_type
        mode = self.pref.mode

        set_index = (mode == show_type)
        if set_index:
            op = layout.operator('wm.context_set_int',
                                 text=self.asset_name,
                                 emboss=False,
                                 )
            op.data_path = self.index_data_path
            op.value = self.index
        else:
            # 只需要显示选中的属性即可
            self.draw_pool(layout)

    @property
    def index(self) -> int:
        """索引
        TODO 调用多的话应该会卡,需要做缓存
        """
        assets = self.find_assets_(self.pref, self.asset_type.lower())
        index = self.get_index_collection_(self, assets)
        return index

    @property
    def index_data_path(self) -> str:
        """获取操作符数据数路径
        当前资产的活动项索引值
        """
        from . import AddonProperty
        pref = f'preferences.addons["{AddonProperty.bl_idname}"].preferences'
        prop = f'.asset_pointer.asset_items.asset_{self.asset_type.lower()}_index'
        return pref + prop


class AssetSelected:
    """选择属性
    主要功能有两种
    1.为每种模式存储渲染状态,避免每一种模式的选择属性共用
    2.在场景模式下选择物体时可以添加选择集,这样可以用于在资产里面进行多种选择的组合
    """

    def update_selected(self, context: 'bpy.context') -> None:
        """
        更新选中状态
        TODO 设置文件夹树状态
            if event.shift:  # 多选,不
                ...
            else:  # 单选,将其它的都清掉
                for asset in self.active_assets:
                    if asset != self:
                        asset.selected = False
        """
        pointer = self.asset_items_pointer_

        if pointer.is_updating:
            return
        try:
            pointer.is_updating = True
            self.update_tree()
        finally:
            pointer.is_updating = False
            region = context.region

            if region:
                region.tag_redraw()

    def get_selected_collect_(self, name) -> bool:
        """通过输入的属性名称,反回当前实例有没有这个属性,如果有就反回,
        没有就反回False
        """
        if name in self:
            return self[name]
        return False

    def get_selected_collect(self):
        """更新选择集"""
        name = self.active_selected_collect_name
        return self.get_selected_collect_(name)

    def set_selected_collect(self, value):
        """设置选择集"""
        name = self.active_selected_collect_name
        self[name] = value

    def get_selected(self) -> bool:
        if self.is_use_selected_collect:
            return self.get_selected_collect()
        mode = self.pref.mode
        key = f'selected_{mode}'
        active = getattr(self, f'active_{mode.lower()}', None)
        if (mode == self.asset_type) and (active == self):
            # 在单选模式,然后获取选中项,反回True
            return True
        elif key in self:
            return self[key]
        return False  # 默认值

    def set_selected(self, value):
        if self.is_use_selected_collect:
            return self.set_selected_collect(value)
        mode = self.pref.mode
        key = f'selected_{mode}'
        self[key] = value

    selected: BoolProperty(name='已选中',
                           update=update_selected,
                           get=get_selected,
                           set=set_selected,
                           )


class AssetItem(PropertyGroup,
                Preferences,

                AssetSelected,
                AssetDraw,
                ):
    """name
    资产项
    """
    from . import asset_tree

    @property
    def icon(self) -> 'bpy.types.ImagePreview':
        """
        TODO 反回资产项图标
        """
        if self.icon_path and (self.icon_path in previews_icons):
            return previews_icons[self.icon_path]
        return self._get_none_icon()

    @property
    def asset_name(self) -> str:
        """反回资产的名称"""
        return os.path.splitext(os.path.basename(self.asset_path))[0]

    @property
    def tree_pointer(self) -> 'asset_tree.TreePointer':
        """反回树pointer"""
        return self.asset_tree_pointer_

    @property
    def trees(self) -> 'list[asset_tree.FolderTree]':
        """反回当前资产所在的树集合"""
        key = self.tree_asset_key_(self.asset_type)
        return getattr(self.tree_pointer, key)

    @property
    def parent_trees(self) -> 'list[asset_tree.FolderTree]':
        """反回所有父项树列表
        迭代
        TODO反向控制树的启用和禁用
        如果当前项是最后一个则又要判断是不是树的所有项都被选中了
        """
        return [tree for tree in self.trees if tree.folder_path in self.folder_path]

    def update_tree(self) -> None:
        """通过更改资产项的选中属性来同时更新树的选中属性
        is_updating = True
        path = self.asset_path
        ass_t = self.asset_type
        folder = self.folder_path
        """
        point = self.tree_pointer
        if not self.selected:  # 如果禁用那就把树的所有选中状态设置False
            point.is_updating = True
            for tree in self.parent_trees:
                tree.selected = self.selected
            point.is_updating = False

    @property
    def search_asset(self) -> bool:
        """反回此资产是否在搜索结果里面的布尔值
        用于显示搜索和过滤的资产项
        """
        name = self.asset_name.lower()
        search = self.pref.search_asset_string.lower()
        if search:
            return search in name
        return True

    def get_show(self) -> bool:
        in_self = ('show' in self)
        return (self['show'] if in_self else True) and self.search_asset

    def set_show(self, value) -> None:
        self['show'] = value

    is_show: BoolProperty(
        name='是否显示',
        default=True,
        get=get_show,
        set=set_show,
    )

    folder_path: StringProperty(name='文件夹路径',
                                subtype='DIR_PATH')

    icon_path: StringProperty(name='图标路径',
                              subtype='FILE_PATH')

    asset_path: StringProperty(name='资产路径',
                               subtype='FILE_PATH')

    asset_type: StringProperty(name='资产的类型')

    @staticmethod
    @cache
    def _byte_to_mb(size: float) -> float:
        """字节转mb"""
        return size / 1024 / 1024

    @staticmethod
    @cache
    def _byte_to_gb(size: float) -> float:
        """字节转gb
        """
        return size / 1024 / 1024 / 1024

    def get_size(self):
        """获取文件大小 mb"""
        if 'asset_size' in self:
            return AssetItem._byte_to_gb(self['asset_size'])
        return 0

    def set_size(self, value):
        """设置大小"""
        self['asset_size'] = value

    asset_size: FloatProperty(name='文件大小',
                              description='文件的大小,用于评估导出的时间,设置单位为byte,输出单位为gb',
                              get=get_size,
                              set=set_size,
                              )


class SelectCollectItem(PropertyGroup, Preferences):
    """选择集
    仅使用在场景模式
    选择物体


    调用属性在资产选择属性和匹配渲染时
    """
    key = 'collect_name'

    def _get_name(self):
        return self.name

    def _set_name(self, value):
        """change
        更改所有包含的项
        1.先添加新名称的选择属性
        2.删除旧名称属性,避免更改几次后数据过多
        """
        keys = self.pref.selected_collect_items.keys()
        name = self._get_name()
        old_name = self.selected_collect_name_(name)
        new_name = self.selected_collect_name_(value)

        if value and (value in keys):
            return

        for item in self.pref.active_objects:
            if old_name in item:
                item[new_name] = item[old_name]
                del item[old_name]

        self.name = self[self.key] = value

    @property
    def index(self) -> int:
        """反回当前项的索引值"""
        return self.selected_collect_items.values().index(self)

    def set_as_active(self):
        """将当前项设置为活动项        """
        self.asset_items_pointer_.asset_select_collect_index = self.index

    collect_name: StringProperty(name='选择集名称',
                                 get=_get_name,
                                 set=_set_name,
                                 )

    def draw(self, layout: 'bpy.types.UILayout', ui_list: 'bpy.types.UIList', index: int) -> None:
        sp = layout.split(factor=0.7)
        row = sp.row(align=True)
        row.alert = not bool(self.collect_name)
        row.prop(self, 'collect_name', text='')

        ro = row.row()
        ro.emboss = 'NONE_OR_STATUS'
        ro.operator(SelectCollectItem.Del.bl_idname,
                    icon='X', text='').index = index

        sp.separator()

    @property
    def all_selected(self) -> 'list[SelectCollectItem]':
        """反回所有的选择子项"""
        name = self.collect_name
        return list(obj for obj in self.active_objects if obj.get_selected_collect_(name))

    @property
    def all_selected_path(self) -> 'list[str]':
        """反回所有的选择子项"""
        return list(i.asset_path for i in self.all_selected)

    class Add(bpy.types.Operator, Preferences):
        bl_idname = 'render.add_render_selected_collect'
        bl_label = '添加选择集'

        def execute(self, context):
            col = self.pref.asset_items_pointer_.asset_select_collect
            add = col.add()
            add.name = "选择集"
            return {'FINISHED'}

    class Del(bpy.types.Operator, Preferences):
        bl_idname = 'render.del_render_selected_collect'
        bl_label = '删除选择集'

        index: IntProperty()

        def execute(self, context):
            col = self.pref.asset_items_pointer_.asset_select_collect
            col.remove(self.index)
            return {'FINISHED'}


class AssetItemsPointer(PropertyGroup, Preferences):
    asset_scene_items: CollectionProperty(name='场景',
                                          type=AssetItem)
    asset_object_items: CollectionProperty(name='物体',
                                           type=AssetItem,
                                           )
    asset_select_collect: CollectionProperty(name='选择集',
                                             type=SelectCollectItem)

    def update_asset_show(self, context):
        """更新资产显示
        """

    def update_asset(self, context):
        """更新资产时重绘一下"""
        log.info(f"update_asset {self}")
        context.area.tag_redraw()

    def update_scene_index(self, context):
        """更新资产索引值"""
        if self.is_scene_mode:
            self.update_queue_scene()
        self.update_asset(context)

    def update_select_collect(self, context):
        """更新选择集"""
        log.info(f"update_select_collect {self}")
        area = context.area

        if area:
            area.tag_redraw()

    def get_select_index(self):
        if 'select_index' in self:
            return self['select_index']
        return 114514

    def set_select_index(self, value):
        """设置选择集索引
        当连续设置索引相同时会更改为-1 索引
        """
        if ('select_index' in self) and (self['select_index'] == value):
            self['select_index'] = -1
            return
        self['select_index'] = value

    asset_object_index: IntProperty(
        description='如果两次设置的值是一样的则设为-1 ，-1表示不启用这个分类设置',
        update=update_asset,
    )
    asset_scene_index: IntProperty(
        update=update_scene_index,
    )
    asset_select_collect_index: IntProperty(update=update_select_collect,
                                            get=get_select_index,
                                            set=set_select_index, )

    def matching_thumbnail(self, item: 'AssetItem', path: str, blend_file: str, files: list[str]) -> None:
        """
        匹配缩略图
        如果文件夹里面有的话就直接赋值
        """
        from os.path import splitext

        for file in files:
            is_thumbnail = self.is_thumbnail(file)  # 是缩略图
            is_matching = (splitext(file)[0] ==
                           splitext(blend_file)[0])  # 是匹配的
            if is_thumbnail and is_matching:
                icon_path = os.path.join(path, file)
                item.icon_path = icon_path
                self.load_icon(icon_path)

    @staticmethod
    def file_size(item: 'AssetItem', path: 'str'):
        """读取文件的大小,用于评估导出时间
        只需第一次导入时读一下就好了"""
        stat = os.stat(path)
        if stat:
            item.asset_size = stat.st_size

    def add(self, dir_path: str, files: 'list[str]', category: str):
        """
        优化算法,将已经找过的图片存起来,不用再循环一次
        查找是否有匹配的图标和资产
        """
        items = getattr(self, f'asset_{category.lower()}_items')
        log.info(f"{dir_path, files, category, items}")

        for file in files:
            file_path = os.path.join(dir_path, file)
            if self.is_asset(file, category):
                log.debug(f'{file} is_asset')
                item = items.add()
                item.asset_type = category
                item.name = item.asset_path = file_path
                item.folder_path = dir_path
                self.file_size(item, file_path)
                self.matching_thumbnail(item, dir_path, file, files)
            else:
                log.debug(f'{file} else')


class_tuple = (
    SelectCollectItem,
    SelectCollectItem.Add,
    SelectCollectItem.Del,

    AssetItem,
    AssetItemsPointer,
)

register_class, unregister_class = bpy.utils.register_classes_factory(
    class_tuple)


def register():
    register_class()


def unregister():
    unregister_class()
